// ============================== 导入依赖 ============================== //

import IS from './is'

// ============================== 导出公共工具 ============================== //

export default {
  /**
   * 空方法
   */
  doNothing: () => null,

  /**
   * 深度复制
   *
   * @param {*} data 原始数据
   *
   * @returns {*} 复制数据
   */
  clone(data) {
    return deepClone(data)
  },

  /**
   * 深度合并
   *
   * @param {Object} result 结果
   * @param {Object} object1 对象一
   * @param {Object} object2 对象二
   *
   * @returns {Object} 最终结果
   */
  merge(result = {}, ...objects) {
    return deepMerge(result, ...objects)
  },

  /**
   * 配置叠加 (兄弟 abc.js + 子目录 abc/index.js)
   *
   * 例一： ...CORE.config(require.context('./')),
   * 例一： ...CORE.config(MyConfig, require.context('./')),
   * 例一： ...CORE.config(require.context('../config'), require.context('./')),
   *
   * @param {*} arg1 参数1 (可为对象，可为 imports 方法)
   * @param {*} arg2 参数2 (同上)
   * @param {*} argn 参数N (同上)
   *
   * @returns {Object} config 配置
   */
  config(...args) {
    return unionConfig(args)
  },

  /**
   * 仓库融合 (谁在后谁生效，遇对象则融合 : 例如 param)
   *
   * 例一： CORE.store(CORE.STORE.WEB, require.context('./'))
   * 例二： CORE.store(CORE.STORE.WEB, CORE.STORE.SELECT, require.context('./'))
   *
   * @param {Object} store1  仓库一
   * @param {Object} store2  仓库二
   * @param {Object} imports 目录导入
   *
   * @returns {Object} { state, getters, mutations, actions, modules, namespaced: true }
   */
  store(...args) {
    return unionStore(args)
  },

  /**
   * 复制到剪贴板
   */
  async copy(text) {
    if (navigator.clipboard) {
      await navigator.clipboard.writeText(text)
      this.MSG.success('已复制到剪贴板')
    } else {
      this.MSG.error('复制失败')
    }
  },

  /**
   * 防抖函数
   *
   * @param {Object} component 组件
   * @param {Object} options 配置
   *
   * @returns {Object} 新组件
   */
  debounce(method, times = 200) {
    let timer
    return function (...args) {
      if (timer) clearTimeout(timer)
      timer = setTimeout(() => method.apply(this, args), times)
    }
  },
}

// ============================== 内部方法 ============================== //

/**
 * 深度复制
 *
 * @param {*} data 原始数据
 * @param {*} hash HASH集合
 *
 * @returns {*} data 复制数据
 */
const deepClone = (data, hash = new WeakMap()) => {
  if (!data) return data // 空值

  if (IS.isDate(data)) return new Date(data) // 日期
  if (IS.isRegExp(data)) return new RegExp(data) // 正则

  if (!IS.isArray(data) && !IS.isJSON(data)) return data // 非数组和非纯粹对象

  if (hash.get(data)) return hash.get(data) // 循环引用

  const cloneObj = new data.constructor()
  hash.set(data, cloneObj)
  for (const key in data) {
    // eslint-disable-next-line no-prototype-builtins
    if (data.hasOwnProperty(key)) cloneObj[key] = deepClone(data[key], hash)
  }
  return cloneObj
}

/**
 * 深度合并 ( null 和 undefined 的值，不会覆盖 Array 和 Object 类型的字段)
 *
 * @param {Object} result 结果
 * @param {Object} object1 对象一
 * @param {Object} object2 对象二
 *
 * @returns {Object} 最终结果
 */
const deepMerge = (result = {}, ...objects) => {
  objects.forEach((object) => {
    if (IS.isJSON(object)) {
      Object.keys(object).forEach((key) => {
        const [init, value] = [result[key], object[key]]

        const isJsonArray = IS.isJSON(init) || IS.isArray(init)
        const isEmpty = value === null || value === undefined
        if (isJsonArray && isEmpty) return // 对象和数组，不被空值覆盖

        result[key] = IS.isJSON(value) ? deepMerge(result[key] || {}, value) : deepClone(value)
      })
    }
  })
  return result
}

/**
 * 配置叠加
 *
 * @param {Array} args 参数列表
 *
 * @returns {Object} config 配置
 */
const unionConfig = (args) => {
  const config = {}
  args.forEach((arg) => {
    if (IS.isFunction(arg)) {
      arg.keys().forEach((path) => {
        const brotherJs = /^\.\/(?!index)[\w-]+\.[jt]s$/.exec(path)
        const folderIndex = /^\.\/[\w-\s]+\/index\.[jt]s$/.exec(path)
        if (brotherJs || folderIndex) deepMerge(config, arg(path).default)
      })
    } else if (IS.isJSON(arg)) {
      deepMerge(config, arg)
    }
  })
  return config
}

/**
 * 仓库融合
 *
 * @param {Array} args 参数列表
 *
 * @returns {Object} store
 */
const unionStore = (args) => {
  const myStore = getMyStore(args.pop())
  args.push(myStore)

  const stateObj = {}
  args.forEach(({ state }) => {
    if (IS.isFunction(state)) deepMerge(stateObj, state())
    else if (IS.isJSON(state)) deepMerge(stateObj, state)
  })

  const state = () => deepClone(stateObj)
  const getters = args.reduce((obj, item) => deepMerge(obj, item.getters), {})
  const mutations = args.reduce((obj, item) => deepMerge(obj, item.mutations), {})
  const actions = args.reduce((obj, item) => deepMerge(obj, item.actions), {})

  const modules = args.reduce((obj, item) => {
    Object.keys(item.modules || {}).forEach((key) => {
      obj[key] = unionStore([obj[key] || {}, item.modules[key]])
    })
    return obj
  }, {})

  return { state, getters, mutations, actions, modules, namespaced: true }
}

/**
 * 获取自定义对象
 *
 * @param {*} arg 参数
 *
 * @returns {Object} 自定义对象
 */
const getMyStore = (arg) => {
  if (IS.isJSON(arg)) return arg
  if (!IS.isFunction(arg)) return {}

  const getObj = (path) => arg(path).default || {}
  const store = { state: {}, getters: {}, mutations: {}, actions: {}, modules: {} }
  const folderModules = {}

  arg.keys().forEach((path) => {
    const state = /^\.\/state\.[jt]s$/.exec(path)
    const getters = /^\.\/getters\.[jt]s$/.exec(path)
    const mutations = /^\.\/mutations\.[jt]s$/.exec(path)
    const actions = /^\.\/actions\.[jt]s$/.exec(path)
    const modules = /^\.\/modules\.[jt]s$/.exec(path)
    const module = /^\.\/([\w-]+)\/index\.[jt]s$/.exec(path)
    if (state) store.state = getObj(path)
    if (getters) store.getters = getObj(path)
    if (mutations) store.mutations = getObj(path)
    if (actions) store.actions = getObj(path)
    if (modules) store.modules = getObj(path)
    if (module) folderModules[module[1]] = getObj(path)
  })

  Object.keys(folderModules).forEach((key) => {
    if (store.modules[key]) store.modules[key] = unionStore([store.modules[key], folderModules[key]])
    else store.modules[key] = folderModules[key]
  })

  return store
}
